package FlowGram.Application.Parser.Actions.Control;


import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import FlowGram.Application.Application;
import FlowGram.Application.Parser.FinchParser;
import FlowGram.Application.Parser.FinchParserException;
import FlowGram.Application.Parser.Controllers.FinchController;
import FlowGram.Application.Parser.Utility.Configuration;


/**
 * 
 * @author thackray
 *
 */
public class FunctionDefinition extends ControlAction {

	
	protected String name;
	
	
	public FunctionDefinition(FinchParser parser) {
		super(parser);
	}
	public FunctionDefinition() {
		super();
	}
	
	public static boolean matches(String s){
		return s.matches("^begin.+");
	}
	public static boolean matchsEnd(String s){
		return s.matches("^end$");
	}
	public static String syntax(){
		return "begin <function_name>\n\t<code>\nend";
	}
	@Override
	public void execute(FinchController controller) {
		incrementFunctionCount(this.name);
		this.ensureController(controller);
		this.innerFlow.execute();
		decrementFunctionCount(this.name);
	}
	public String getName(){
		return this.name;
	}
	public void setName(String name){
		this.name = name;
	}
	@Override
	public String logMessage() {
		return null;
	}

	

	@Override
	public String toCode(int indent) {
		return "begin "+this.name+"\n"+this.innerFlow.toCode(indent+1)+"\n"+this.getIndent(indent)+"end";
	}
	@Override
	public String toHTML(String idPrefix){
		return "begin "+this.name+""+this.innerFlow.toHTML(idPrefix)+"end";
	}
	
	@Override
	public void parse(String statement) throws FinchParserException {
		Pattern p = Pattern.compile("^begin\\s+(\\w+)$");
		Matcher matcher = p.matcher(statement);
		while(matcher.find()){
			if(matcher.group(1) != null){
				this.name = matcher.group(1);
				String buffer = "";
				String line = this.parser.getNextStatement();
				while(!matchsEnd(line)){
					buffer += line+"\n";
					line = this.parser.getNextStatement();
				}
				FinchParser parse = new FinchParser();
				registerFunction(this);
				this.innerFlow = parse.Parse(buffer);
				
			}	
		}
	}
	
	
	
	
	
	
	
	
	private static Hashtable<String, FunctionDefinition> functions;
	private static Hashtable<String, Integer> functionCount;
	private static Integer maxRecursion = 20;
	
	public static FunctionDefinition findFunction(String functionname){
		ensureFunctionMap();
		if(functions.containsKey(functionname)){
			return functions.get(functionname);
		}
		return null;
	}
	public static Collection<FunctionDefinition> getAllFunctions(){
		ensureFunctionMap();
		return functions.values();
	}
	public static Collection<String> getAllFunctionNames(){
		ensureFunctionMap();
		return functions.keySet();
	}
	/**
	 * 
	 * @param function
	 * @return	Boolean returns true if the function was successfuly registered. returns false if the function was already registered.
	 */
	public static Boolean registerFunction(FunctionDefinition function){
		ensureFunctionMap();
		if(!functions.containsKey(function.getName())){
			functions.put(function.getName(), function);
			return true;
		}
		return false;
	}
	private static void ensureFunctionMap(){
		if(functions == null){
			try{
				Configuration config = Configuration.getInstance();
				maxRecursion = config.config.getInt("robot.movetime");
			}catch(Exception e){
				e.printStackTrace();
			}
			
			functions = new Hashtable<String, FunctionDefinition>();
		}
	}
	private static void ensureFunctionCallMap(){
		if(functionCount == null){
			functionCount = new Hashtable<String, Integer>();
		}
	}
	public static void incrementFunctionCount(String functionName) {
		ensureFunctionCallMap();
		Integer curr;
		if(functionCount.containsKey(functionName)){
			curr = functionCount.get(functionName)+1;
		}else{
			curr = 1;
		}
		functionCount.put(functionName, curr);
		if(curr > maxRecursion){
			throw new RuntimeException("Function \""+functionName+"\" has exceeded the max recursion limit ("+maxRecursion+")! (recusion #"+curr+" times)");
		}
	}
	public static void decrementFunctionCount(String functionName){
		ensureFunctionCallMap();
		if(functionCount.containsKey(functionName)){
			Integer curr = functionCount.get(functionName) - 1;
			if(curr < 0){ curr = 0; }
			functionCount.put(functionName, curr);
		}
	}
	

}
